/*
 * LDIR
 *
 * LDIR, a GPS cartography application built for Let's Do It Romania eco
 * campaign.
 * Copyright (C) 2010 Genera Software <office@generasoftware.com>
 *
 * http://generasoftware.com/
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
// INCLUDE FILES
// Class include
#include <string.h>
#include <assert.h>
#include <s32mem.h>
#include "Entity.h"

EXPORT_C CEntity* CEntity::NewLC( Entity_type aType )
	{
	CEntity* me = new ( ELeave ) CEntity( aType );
	CleanupStack::PushL( me );
	return me;
	}

EXPORT_C CEntity* CEntity::NewL( Entity_type aType )
	{
	CEntity* me = CEntity::NewLC( aType );
	CleanupStack::Pop( me );
	return me;
	}

CEntity::CEntity( Entity_type aType ) :
	iType( aType )
	{

	}

EXPORT_C CEntity::~CEntity()
	{
	switch ( iType )
		{
		case str_type:
			delete iVariant.iHbuf8;
			break;
		case pair_type:
			delete iVariant.iPair;
			break;
		case array_type:
		case obj_type:
			if ( iVariant.iEntities )
				{
				iVariant.iEntities->ResetAndDestroy();
				delete iVariant.iEntities;
				}
			break;
		default:
			break;
		}
	}

EXPORT_C void CEntity::SetValueL( const char* value )
	{
	iType = str_type;
	delete iVariant.iHbuf8;
	iVariant.iHbuf8 = HBufC8::NewL( strlen( value ) );
	iVariant.iHbuf8->Des().Copy( ( TUint8* ) value );
	}

EXPORT_C void CEntity::SetValueL( const TDesC8& value )
	{
	iType = str_type;
	delete iVariant.iHbuf8;
	iVariant.iHbuf8 = value.AllocL();
	}

EXPORT_C void CEntity::SetValueL( bool value )
	{
	iType = bool_type;
	iVariant.b = value;
	}

EXPORT_C void CEntity::SetValueL( TInt value )
	{
	iType = int_type;
	iVariant.i = value;
	}

EXPORT_C void CEntity::SetValueL( TReal value )
	{
	iType = real_type;
	iVariant.real = value;
	}

EXPORT_C void CEntity::SetValueL( CPair* value )
	{
	iType = pair_type;
	iVariant.iPair = value;
	}

EXPORT_C void CEntity::SetValueL( Entities *value )
	{
	iVariant.iEntities = value;
	}

EXPORT_C TInt CEntity::Type() const
	{
	return iType;
	}

EXPORT_C TBool CEntity::IsRoot() const
	{
	return iParent ? EFalse : ETrue;
	}

EXPORT_C CEntity* CEntity::Parent()
	{
	return iParent;
	}

EXPORT_C void CEntity::AddChildL( CEntity* aChild )
	{
	__ASSERT_ALWAYS(((Type() == array_type) || (Type() == obj_type)), User::Invariant());
	if ( !iVariant.iEntities )
		iVariant.iEntities = new ( ELeave ) CArrayPtrFlat<CEntity> ( 4 );

	iVariant.iEntities->AppendL( aChild );
	aChild->SetParent( this );
	}
EXPORT_C void CEntity::SetParent( CEntity* aParent )
	{
	iParent = aParent;
	}

EXPORT_C TBool CEntity::operator==( const CEntity& lhs ) const
	{
	if ( this == &lhs )
		return ETrue;

	if ( Type() != lhs.Type() )
		return EFalse;

	switch ( iType )
		{
		case str_type:
			return GetStr() == lhs.GetStr();
		case bool_type:
			return GetBool() == lhs.GetBool();
		case real_type:
			return GetReal() == lhs.GetReal();
		case pair_type:
			return GetPair() == lhs.GetPair();
		case int_type:
			return GetInt() == lhs.GetInt();
		case obj_type:
		case array_type:
			return GetArray() == lhs.GetArray();
		case null_type:
			return true;
		};

	return EFalse;
	}

EXPORT_C const TDesC8& CEntity::GetStr() const
	{
	__ASSERT_ALWAYS( Type() == str_type , User::Invariant());
	if ( iVariant.iHbuf8 )
		return *( iVariant.iHbuf8 );
	return KNullDesC8;
	}

EXPORT_C CPair* CEntity::GetPair() const
	{
	__ASSERT_ALWAYS (Type() == pair_type, User::Invariant());
	return iVariant.iPair;
	}

EXPORT_C Entities* CEntity::GetArray() const
	{
	__ASSERT_ALWAYS( Type() == array_type || Type() == obj_type, User::Invariant());
	return iVariant.iEntities;
	}

EXPORT_C bool CEntity::GetBool() const
	{
	__ASSERT_ALWAYS( Type() == bool_type, User::Invariant() );
	return iVariant.b;
	}

EXPORT_C TInt CEntity::GetInt() const
	{
	__ASSERT_ALWAYS( Type() == int_type, User::Invariant() );
	return iVariant.i;
	}

EXPORT_C TReal CEntity::GetReal() const
	{
	__ASSERT_ALWAYS( Type() == real_type, User::Invariant() );
	return iVariant.real;
	}

EXPORT_C void CEntity::DeleteEntity( TInt aKeyNameIndex )
	{
	if ( ( Type() == obj_type || Type() == array_type ) && iVariant.iEntities )
		{
		TInt index = KErrNotFound;
		for ( TInt i = 0; i < iVariant.iEntities->Count(); ++i )
			if ( ( *iVariant.iEntities )[i]->Type() == pair_type )
				{
				CPair* pair = ( *iVariant.iEntities )[i]->GetPair();
				if ( pair && pair->iFieldIndex == aKeyNameIndex )
					{
					index = i;
					break;
					}
				}
		if ( KErrNotFound != index )
			{
			delete ( *iVariant.iEntities )[index];
			iVariant.iEntities->Delete( index );
			}
		}
	}

EXPORT_C CEntity* CEntity::GetEntity( TInt aKeyNameIndex ) const
	{
	if ( Type() == pair_type && iVariant.iPair->iFieldIndex == aKeyNameIndex )
		{
		return iVariant.iPair->iValue;
		}
	else if ( ( Type() == array_type || Type() == obj_type )
		&& iVariant.iEntities )
		{
		for ( TInt i = 0; i < iVariant.iEntities->Count(); ++i )
			if ( ( *iVariant.iEntities )[i]->Type() == pair_type )
				{
				CPair* pair = ( *iVariant.iEntities )[i]->GetPair();
				if ( pair && pair->iFieldIndex == aKeyNameIndex )
					return pair->iValue;
				}
		}
	return NULL;
	}

EXPORT_C void CEntity::ExternalizeL( RWriteStream& aStream ) const
	{
	aStream.WriteInt32L( iType );
	switch ( iType )
		{
		case int_type:
			aStream.WriteInt32L( iVariant.i );
			return;
		case bool_type:
			aStream.WriteInt32L( iVariant.b );
			return;
		case real_type:
			aStream.WriteReal32L( iVariant.real );
			return;
		case str_type:
			aStream.WriteInt32L( iVariant.iHbuf8->Length() );
			aStream.WriteL( *iVariant.iHbuf8, iVariant.iHbuf8->Length() );
			return;
		case pair_type:
			aStream << *iVariant.iPair;
			return;
		case array_type:
		case obj_type:
			if ( !iVariant.iEntities )
				{
				aStream.WriteInt32L( 0 );
				return;
				}
			aStream.WriteInt32L( iVariant.iEntities->Count() );
			for ( TInt i = 0; i < iVariant.iEntities->Count(); ++i )
				aStream << *( ( *iVariant.iEntities )[i] );
			return;
		case null_type:
			return;
		default:
			User::Invariant();
			break;
		}
	}

EXPORT_C void CEntity::InternalizeL( RReadStream& aStream )
	{
	iType = aStream.ReadInt32L();
	switch ( iType )
		{
		case int_type:
			iVariant.i = aStream.ReadInt32L();
			return;
		case bool_type:
			iVariant.b = aStream.ReadInt32L();
			return;
		case real_type:
			iVariant.real = aStream.ReadReal32L();
			return;
		case str_type:
			{
			TInt length = aStream.ReadInt32L();
			iVariant.iHbuf8 = HBufC8::NewL( length );
			TPtr8 ptr = iVariant.iHbuf8->Des();
			aStream.ReadL( ptr, length );
			}
			return;
		case pair_type:
			{
			CPair* pair = CPair::NewL();
			aStream >> *pair;
			iVariant.iPair = pair;
			}
			return;
		case array_type:
		case obj_type:
			{
			if ( !iVariant.iEntities )
				iVariant.iEntities = new ( ELeave ) CArrayPtrFlat<CEntity> ( 2 );

			TInt count = aStream.ReadInt32L();
			for ( TInt i = 0; i < count; ++i )
				{
				CEntity* entity = CEntity::NewLC( null_type );
				aStream >> *entity;
				iVariant.iEntities->AppendL( entity );
				CleanupStack::Pop( entity );
				}
			}
			return;
		case null_type:
			return;
		default:
			User::Invariant();
			break;
		}
	}

EXPORT_C TInt CEntity::Size() const
	{
	TInt size = 0;
	size += sizeof(TInt32); // type
	switch ( iType )
		{
		case int_type:
		case bool_type:
			size += sizeof(TInt32);
			break;
		case real_type:
			size += sizeof(TReal32);
			break;
		case str_type:
			size += sizeof(TInt32);
			size += iVariant.iHbuf8->Length();
			break;
		case pair_type:
			size += iVariant.iPair->Size();
			break;
		case array_type:
		case obj_type:
			size += sizeof(TInt32); // count
			if ( !iVariant.iEntities )
				break;
			for ( TInt i = 0; i < iVariant.iEntities->Count(); ++i )
				size += ( ( *iVariant.iEntities )[i] )->Size(); // size of each entity
			break;
		default:
			User::Invariant();
			break;
		}

	return size;
	}

EXPORT_C CEntity* CEntity::CloneL() const
	{
	CEntity* clone = CEntity::NewLC( null_type );
	HBufC8* buffer = HBufC8::NewLC( Size() );
	TPtr8 bufferPtr = buffer->Des();
	RDesWriteStream writeStream;
	writeStream.Open( bufferPtr );
	CleanupClosePushL( writeStream );
	ExternalizeL( writeStream );
	writeStream.CommitL();
	CleanupStack::PopAndDestroy( &writeStream );
	RDesReadStream readStream;
	readStream.Open( *buffer );
	CleanupClosePushL( readStream );
	clone->InternalizeL( readStream );
	CleanupStack::PopAndDestroy( &readStream );
	CleanupStack::PopAndDestroy( buffer );
	CleanupStack::Pop( clone );
	return clone;
	}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
CPair::CPair()
	{
	}

EXPORT_C CPair::~CPair()
	{
	delete iValue;
	}

EXPORT_C CPair* CPair::NewL()
	{
	CPair* me = new ( ELeave ) CPair;
	return me;
	}

EXPORT_C CPair* CPair::NewL( TInt aFieldIndex, CEntity* aValue )
	{
	CPair* me = new ( ELeave ) CPair;
	CleanupStack::PushL( me );
	me->iFieldIndex = aFieldIndex;
	me->iValue = aValue;
	CleanupStack::Pop( me );
	return me;
	}

EXPORT_C void CPair::SetKeyL( TInt aFieldIndex )
	{
	iFieldIndex = aFieldIndex;
	}

EXPORT_C void CPair::SetValue( CEntity* aValue )
	{
	iValue = aValue;
	}

EXPORT_C TBool CPair::operator==( const CPair& lhs ) const
	{
	if ( this == &lhs )
		return true;

	return ( iFieldIndex == lhs.iFieldIndex ) && ( iValue == lhs.iValue );
	}

EXPORT_C void CPair::ExternalizeL( RWriteStream& aStream ) const
	{
	aStream.WriteInt32L( iFieldIndex );
	aStream << *iValue;
	}

EXPORT_C void CPair::InternalizeL( RReadStream& aStream )
	{
	iFieldIndex = aStream.ReadInt32L();
	if ( iValue )
		delete iValue;

	CEntity* entity = CEntity::NewL( null_type );
	aStream >> *entity;
	iValue = entity;
	}

EXPORT_C TInt CPair::Size() const
	{
	return sizeof(TInt) + iValue->Size();
	}

// End of File
